﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vibz.Common.Sequential;
using System.Xml.Serialization;
using System.Drawing;

namespace Vibz.Game.Bugs.Model
{
    [Serializable]
    [XmlType(TypeName = "cm")]
    public class Compartment : GroundObject, IElement, IMovingObject
    {
        int _seqID = -1;
        [XmlElement("sid")]
        public int SequenceID { get { if (this.Sequence != null) return this.Sequence.First.ID; else return _seqID; } set { _seqID = value; NotifyValueChanged("SequenceID", value); } }

        [XmlIgnore]
        public ElementSequence Sequence { get; set; }

        int _engineID = -1;
        [XmlElement("eid")]
        public int EngineID { get { if (this.Engine != null)  return this.Engine.ID; else return _engineID; } set { _engineID = value; NotifyValueChanged("EngineID", value); } }

        [XmlIgnore]
        public Player Engine { get; set; }

        Orientation previousOrientation;
        
        [XmlElement("po")]
        public Orientation PreviousOrientation
        {
            get
            {
                if (previousOrientation == null) previousOrientation = 
                    new Orientation(this, new Rectangle(this.Location, new Size(this.Thickness, this.Thickness)), this.Direction); 
                return previousOrientation;
            }
            set
            {
                previousOrientation = value;
                // No Notify
                // NotifyValueChanged("PreviousOrientation", value);
            }
        }

        public Compartment() { }
        public Compartment(Ground gnd) : base(gnd)
        { }

        public IElement Previous 
        { 
            get 
            {
                if (Sequence == null)
                    return null;
                IElement retValue = Sequence.PreviousOf(this);
                if (retValue == null)
                    return Engine;
                else 
                    return retValue;

            }
        }
        public IElement Next
        {
            get
            {
                if (Owner == null)
                    return null;
                return Sequence.NextTo(this);
            }
        }

        public override void MergeOnPlayer(Player player)
        {
            player.AddCompartment(this);
            player.Score += 50;
        }
        MoveDirection _dir = MoveDirection.Up;
        [XmlElement("d")]
        public MoveDirection Direction
        {
            get
            {
                return _dir;
            }
            set
            {
                if (_dir == MoveDirection.NA || _dir == value)
                    return; 
                _dir = value;
                NotifyValueChanged("Direction", value);
            }
        }

        void AllignWith(Point point, MoveDirection direction, int thickness)
        {
            switch (direction)
            {
                case MoveDirection.Up:
                    this.Location = new Point(
                        point.X - ((this.Thickness - thickness) / 2),
                        ((GroundObject)Previous).Location.Y + thickness);
                    break;
                case MoveDirection.UpRight:
                    this.Location = new Point(
                        point.X - ((this.Thickness - thickness) / 2),
                        point.Y - ((this.Thickness - thickness) / 2));
                    break;
                case MoveDirection.Right:
                    this.Location = new Point(
                        ((GroundObject)Previous).Location.X - this.Thickness,
                        point.Y - ((this.Thickness - thickness) / 2));
                    break;
                case MoveDirection.DownRight:
                    this.Location = new Point(
                        point.X - ((this.Thickness - thickness) / 2),
                        point.Y - ((this.Thickness - thickness) / 2));
                    break;
                case MoveDirection.Down:
                    this.Location = new Point(
                        point.X - ((this.Thickness - thickness) / 2),
                        ((GroundObject)Previous).Location.Y - this.Thickness);
                    break;
                case MoveDirection.DownLeft:
                    this.Location = new Point(
                        point.X - ((this.Thickness - thickness) / 2),
                        point.Y - ((this.Thickness - thickness) / 2));
                    break;
                case MoveDirection.Left:
                    this.Location = new Point(
                        ((GroundObject)Previous).Location.X + thickness,
                        point.Y - ((this.Thickness - thickness) / 2));
                    break;
                case MoveDirection.UpLeft:
                    this.Location = new Point(
                        point.X - ((this.Thickness - thickness) / 2),
                        point.Y - ((this.Thickness - thickness) / 2));
                    break;
            }
        }

        public void Reposition()
        {
            try
            {
                GroundObject prevObj = (GroundObject)Previous;
                Orientation prevObjOrn = ((IMovingObject)prevObj).PreviousOrientation;
                if (this.Direction != ((IMovingObject)prevObj).Direction)
                {
                    double actSpace = Math.Sqrt(Math.Pow(prevObj.Center.X - this.Center.X, 2) + Math.Pow(prevObj.Center.Y - this.Center.Y, 2));
                    double reqSpace = Math.Sqrt(Math.Pow((prevObj.Thickness + this.Thickness) / 2, 2) + Math.Pow((prevObj.Thickness + this.Thickness) / 2, 2));
                    if (actSpace < reqSpace)
                        return;
                }

                AllignWith(
                    prevObjOrn.Rectangle.Location,
                    prevObjOrn.Direction,
                    prevObj.Thickness);
                //if (prevObj != null)
                //    this.Direction = prevObjOrn.Direction;

                if (this.Direction != ((IMovingObject)prevObj).Direction)
                {
                    double actSpace = Math.Sqrt(Math.Pow(prevObj.Center.X - this.Center.X, 2) + Math.Pow(prevObj.Center.Y - this.Center.Y, 2));
                    double reqSpace = Math.Sqrt(Math.Pow((prevObj.Thickness + this.Thickness) / 2, 2) + Math.Pow((prevObj.Thickness + this.Thickness) / 2, 2));
                    if (actSpace > (1.5 * reqSpace))
                    {
                        AllignWith(
                            prevObj.Location,
                            ((IMovingObject)prevObj).Direction,
                            prevObj.Thickness);
                        //this.Direction = ((IMovingObject)prevObj).Direction;
                    }
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }
        public override string ToString()
        {
            return base.ToString() + ", Direction:" + this.Direction.ToString() + ", Prev Direction:" + this.PreviousOrientation.Direction.ToString(); 
        }
    }
}
